﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace SilkenWeb.Reg
{
    /// <summary>
    /// Class to manage REG lists.
    /// </summary>
    /// <remarks>
    /// Author: Keith Jackson
    /// Date: July 2008
    /// Copyright 2008 S-Cool Ltd.
    /// </remarks>
    public class CandidateListManager
    {

        #region | Construction |

        /// <summary>
        /// Instantiates a new CandidateListManager.
        /// </summary>
        public CandidateListManager(int reintroductionFactor)
        {
            AllCandidates = new CandidateCollection();
            StepsExecuted = 0;
            ReintroductionFactor = reintroductionFactor;
        }

        #endregion

        #region | Properties & Fields |

        private Random randomizer = new Random();

        /// <summary>
        /// Gets all candidates included for the REG process.
        /// </summary>
        public CandidateCollection AllCandidates { get; private set; } 

        /// <summary>
        /// Gets the total number of REG steps executed.
        /// </summary>
        public long StepsExecuted { get; private set; }

        /// <summary>
        /// Gets or Sets the Reintoduction Factor.
        /// </summary>
        /// <remarks>This is the number of 'spaces' that can be randomly allocated.</remarks>
        public int ReintroductionFactor { get; set; }

        /// <summary>
        /// Gets the Active Candidates.
        /// </summary>
        public ReadOnlyCandidateCollection ActiveCandidates 
        { 
            get { return AllCandidates.ActiveCandidates ;}
        }

        /// <summary>
        /// Gets the Initial Candidates.
        /// </summary>
        public ReadOnlyCandidateCollection InitialCandidates
        {
            get { return new ReadOnlyCandidateCollection(AllCandidates); }
        }

        /// <summary>
        /// Gets the Rejected Candidates.
        /// </summary>
        public ReadOnlyCandidateCollection RejectedCandidates 
        { 
            get { return AllCandidates.RejectedCandidates ;} 
        }

        /// <summary>
        /// Gets the candidate selected by REG.
        /// </summary>
        public Candidate SelectedCandidate { get; private set;  }

        #endregion

        #region | Public Methods |

        /// <summary>
        /// Resets the state of REG.
        /// </summary>
        public void Reset()
        {
            StepsExecuted = 0;
            foreach (Candidate c in AllCandidates)
            {
                c.Rejected = false;
            }
        }

        /// <summary>
        /// Runs through reg until such a time as input is required or a result has been reached.
        /// </summary>
        public RegRunResult Run()
        {
            ExecuteRegStep();
            if (ActiveCandidates.Count == 1)
            {
                SelectedCandidate = ActiveCandidates[0];
                return RegRunResult.CandidateSelected;
            }
            else
            {
                return RegRunResult.InputRequired;
            }
        }

        #endregion

        #region | Private Methods |

        /// <summary>
        /// Executes a step in the reg process recursively until input is required or an item has been selected.
        /// </summary>
        private void ExecuteRegStep()
        {
            if (StepsExecuted != long.MaxValue) StepsExecuted++;

            int activeCount = ActiveCandidates.Count;
            int rejectCount = RejectedCandidates.Count;
            int maxValue = activeCount + ReintroductionFactor;
            int rnd = randomizer.Next(1, maxValue);

            if (rnd <= activeCount)
            {
                ActiveCandidates[rnd - 1].Rejected = true;
                if (activeCount > 2)
                {
                    ExecuteRegStep();
                }
                else
                {
                    return;
                }
            }
            else
            {
                if (rejectCount > 1)
                {
                    return;
                }
                else if (rejectCount == 1)
                { 
                    RejectedCandidates[0].Rejected = false;
                    ExecuteRegStep();
                }
                else
                {
                    ExecuteRegStep();
                }
            }
        }

        #endregion

    }

    #region | Enumerators |

    /// <summary>
    /// Possible REG responses when running.
    /// </summary>
    public enum RegRunResult
    {
        CandidateSelected,
        InputRequired
    }

    #endregion
}
